package com.vcc.web.controller;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.text.csv.*;
import com.mybatisflex.core.paginate.Page;
import com.vcc.core.annotations.*;
import com.vcc.core.exception.ProgramException;
import com.vcc.core.util.FileUtil;
import com.vcc.core.util.IdWorker;
import com.vcc.data.entity.*;
import com.vcc.service.client.vo.taskLog.TaskLogInfoVo;
import com.vcc.service.service.*;
import com.vcc.web.core.queue.batch.close.queue.CloseQueue;
import com.vcc.web.core.queue.batch.freeze.queue.FreezeQueue;
import com.vcc.web.core.queue.batch.out.queue.OutQueue;
import com.vcc.web.core.queue.batch.recharge.queue.RechargeQueue;
import com.vcc.web.core.queue.batch.unFreeze.queue.UnFreezeQueue;
import com.vcc.web.core.util.UserUtil;
import com.vcc.service.client.vo.taskLog.TaskLogVo;
import lombok.extern.log4j.Log4j2;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

@Log4j2
@RestController
@RequestMapping("taskLog")
public class TaskLogController {

    @Autowired
    private UserCardService userCardService;
    @Autowired
    private TaskLogService taskLogService;
    @Autowired
    private WalletService walletService;
    @Autowired
    private UserService userService;
    @Autowired
    private RechargeQueue rechargeQueue;
    @Autowired
    private OutQueue outQueue;
    @Autowired
    private FreezeQueue freezeQueue;
    @Autowired
    private UnFreezeQueue unFreezeQueue;
    @Autowired
    private CloseQueue closeQueue;
    @Autowired
    private ClientParamService clientParamService;
    @Autowired
    private TaskLogInfoService taskLogInfoService;


    public CsvData convertToCsvDataWithConfig(MultipartFile file) throws IOException {
        // 配置CSV读取参数
        CsvReadConfig config = CsvReadConfig.defaultConfig();
        config.setFieldSeparator(','); // 设置分隔符，默认是逗号
        config.setContainsHeader(true); // 是否包含表头
        config.setSkipEmptyRows(true); // 跳过空行
        config.setErrorOnDifferentFieldCount(false); // 字段数量不一致时不报错
        // 创建CsvReader
        CsvReader reader = CsvUtil.getReader(config);
        // 读取CSV数据
        return reader.read(
                new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8)
        );
    }

    public boolean isCsvFile(MultipartFile file) {
        if (file.isEmpty()) {
            return false;
        }

        // 检查Content Type
        String contentType = file.getContentType();
        boolean isCsvContentType = contentType != null &&
                (contentType.equals("text/csv") ||
                        contentType.equals("application/csv") ||
                        contentType.contains("csv"));

        // 检查文件扩展名
        String originalFilename = file.getOriginalFilename();
        boolean isCsvExtension = originalFilename != null &&
                originalFilename.toLowerCase().endsWith(".csv");

        return isCsvContentType || isCsvExtension;
    }

    @RestPack
    @ROReject
    @KycReject
    @SaCheckPermission("batch:request")
    @PostMapping("batch")
    public Boolean batch(
            @RequestParam("useFlag") Integer useFlag,
            @RequestBody MultipartFile file) {
        if (!isCsvFile(file)) {
            throw new ProgramException("文件格式错误,请上传csv格式的文件");
        }
        try {
            User user = UserUtil.get();
            Role role = UserUtil.getRole();
            CsvData data = convertToCsvDataWithConfig(file);
            List<String> header = data.getHeader();
            if (useFlag == 0 || useFlag == 1) {
                if (header == null || header.size() != 2 || !header.contains("cardNumberId") || !header.contains("amount")) {
                    throw new ProgramException("文件头必须包含 cardNumberId,amount 且仅限这两列");
                }
            } else if (useFlag == 2 || useFlag == 3 || useFlag == 4) {
                if (header == null || header.size() != 1 || !header.contains("cardNumberId")) {
                    throw new ProgramException("文件头必须包含 cardNumberId 且仅限这列");
                }
            } else {
                throw new ProgramException("操作类型错误");
            }

            String batchMaxCount = clientParamService.getValueByCodeAndUserIdOrDefaultValue("BATCH_MAX_COUNT", user.getId(), "200");
            if (data.getRowCount() > Integer.parseInt(batchMaxCount)) {
                throw new ProgramException("每次操作限制" + batchMaxCount + "条");
            }
            List<CsvRow> rows = data.getRows();
            List<Long> cardNumberIds = rows.stream().map(it -> Long.valueOf(it.getByName("cardNumberId"))).distinct().toList();
            if (cardNumberIds.isEmpty()) {
                throw new ProgramException("文件内容不能为空");
            }
            if (cardNumberIds.size() != rows.size()) {
                throw new ProgramException("卡ID存在重复");
            }

            if (useFlag == 0 || useFlag == 1) {
                boolean hasNegative = rows.stream()
                        .anyMatch(obj -> new BigDecimal(obj.getByName("amount")).compareTo(BigDecimal.ZERO) < 0);
                if (hasNegative) {
                    throw new ProgramException("金额不能小于0");
                }
            }
            List<UserCard> userCards = userCardService.listByIds(cardNumberIds);
            List<Long> userIds = userCards.stream().map(UserCard::getUserId).distinct().toList();
            for (Long targetUserId : userIds) {
                UserUtil.baseVerifyUser(
                        role,
                        user.getId(),
                        targetUserId,
                        user.getParentId(),
                        userService.getParentIdById(targetUserId)
                );
            }
            //检查文件内金额是否小于0
            /*
              0 充值  1  转出 2  冻结  3 解冻  4 销卡
             */
            if (useFlag.equals(0)) {
                for (Long userId : userIds) {
                    List<UserCard> filterUserCards = userCards.stream().filter(it -> it.getUserId().equals(userId)).toList();
                    //把 filterUserCards 根据币种分组
                    Map<String, List<UserCard>> collect = filterUserCards.stream().collect(Collectors.groupingBy(UserCard::getCurrencyCode));
                    for (Map.Entry<String, List<UserCard>> entry : collect.entrySet()) {
                        String currencyCode = entry.getKey();
                        List<UserCard> cards = entry.getValue();
                        //根据cards 从rows中拿出amount
                        Wallet wallet = walletService.getByUserIdAndCurrencyCode(userId, currencyCode);
                        BigDecimal sumAmount = BigDecimal.valueOf(0);
                        for (CsvRow row : rows) {
                            Long cardNumberId = Long.valueOf(row.getByName("cardNumberId"));
                            BigDecimal amount = new BigDecimal(row.getByName("amount"));
                            Optional<UserCard> first = cards.stream().filter(it -> it.getId().equals(cardNumberId)).findFirst();
                            if (first.isPresent()) {
                                sumAmount = sumAmount.add(amount);
                            }
                        }
                        if (Objects.equals(wallet.getUsableQuota().compareTo(sumAmount), -1)) {
                            throw new ProgramException("您" + wallet.getCurrencyCode() + "钱包余额不足");
                        }
                    }
                }
            }
            String orderId = IdWorker.getStringOrderId();
            TaskLog taskLog = new TaskLog();
            taskLog.setOrderId(orderId);
            taskLog.setUserId(user.getId());
            taskLog.setRequestDate(new Date());
            taskLog.setUseFlag(useFlag);
            taskLog.setRemark("处理中");
            taskLog.setStatus(2);
            taskLogService.save(taskLog);
            List<TaskLogInfo> taskLogInfos = new ArrayList<>();
            for (CsvRow row : rows) {
                UserCard userCard = userCards.stream()
                        .filter(it -> it.getId().equals(Long.valueOf(row.getByName("cardNumberId"))))
                        .findFirst().orElseThrow(() -> new ProgramException("卡ID:" + row.getByName("cardNumberId") + "不存在"));
                TaskLogInfo taskLogInfo = new TaskLogInfo();
                taskLogInfo.setRequestId(IdWorker.getStringOrderId());
                taskLogInfo.setTaskId(taskLog.getId());
                taskLogInfo.setCardTypeId(userCard.getCardTypeId());
                taskLogInfo.setCardNumber(userCard.getCardNumber());
                taskLogInfo.setUserId(userCard.getUserId());
                taskLogInfo.setCardNumberId(userCard.getId());
                taskLogInfo.setAmount(useFlag.equals(0) || useFlag.equals(1) ? new BigDecimal(row.getByName("amount")) : BigDecimal.ZERO);
                taskLogInfo.setStatus(2);
                taskLogInfo.setRemark("处理中");
                taskLogInfo.setChannelName(userCard.getChannelCode());
                taskLogInfo.setRequestDate(new Date());
                taskLogInfoService.save(taskLogInfo);
                taskLogInfos.add(taskLogInfo);
            }
             /*
              0 充值  1  转出 2  冻结  3 解冻  4 销卡
             */
            for (TaskLogInfo taskLogInfo : taskLogInfos) {
                if (useFlag.equals(0)) {
                    rechargeQueue.add(
                            taskLogInfo.getTaskId(),
                            taskLogInfo.getCardNumberId(),
                            taskLogInfo.getId(),
                            taskLogInfo.getAmount(),
                            taskLogInfo.getRequestId()
                    );
                } else if (useFlag.equals(1)) {
                    outQueue.add(
                            taskLogInfo.getTaskId(),
                            taskLogInfo.getId(),
                            taskLogInfo.getCardNumberId(),
                            taskLogInfo.getAmount(),
                            taskLogInfo.getRequestId()
                    );
                } else if (useFlag.equals(2)) {
                    freezeQueue.add(
                            taskLogInfo.getTaskId(),
                            taskLogInfo.getId(),
                            taskLogInfo.getCardNumberId(),
                            taskLogInfo.getRequestId()
                    );
                } else if (useFlag.equals(3)) {
                    unFreezeQueue.add(
                            taskLogInfo.getTaskId(),
                            taskLogInfo.getId(),
                            taskLogInfo.getCardNumberId(),
                            taskLogInfo.getRequestId()
                    );
                } else if (useFlag.equals(4)) {
//                    closeQueue.add(
//                            taskLogInfo.getTaskId(),
//                            taskLogInfo.getId(),
//                            taskLogInfo.getCardNumberId(),
//                            taskLogInfo.getRequestId()
//                    );
                }
//                else if (useFlag.equals(1)) {
//                    outQueue.add(orderId, stringBigDecimalEntry.getKey(), stringBigDecimalEntry.getValue(), index, map.size());
//                } else if (useFlag.equals(2)) {
//                    freezeQueue.add(orderId, stringBigDecimalEntry.getKey(), index, map.size());
//                } else if (useFlag.equals(3)) {
//                    unFreezeQueue.add(orderId, stringBigDecimalEntry.getKey(), index, map.size());
//                } else if (useFlag.equals(4)) {
//                    closeQueue.add(orderId, stringBigDecimalEntry.getKey(), index, map.size());
//                }
            }
            return true;
        } catch (IOException e) {
            log.error("文件读取失败", e);
            throw new ProgramException("文件读取失败");
        }
    }


    @RestPack
    @GetMapping("paging")
    @SaCheckPermission("batch:view")
    public Page<TaskLogVo> paging(
            Integer pageNumber,
            Integer pageSize,
            Integer status,
            Long targetUserId,
            Integer useFlag,
            String startDate,
            String endDate
    ) {
        User user = UserUtil.get();
        Role role = UserUtil.getRole();
        return taskLogService.clientPaging(
                pageNumber,
                pageSize,
                role,
                user.getId(),
                targetUserId,
                status,
                startDate,
                endDate,
                useFlag
        );
    }

    @RestPack
    @GetMapping("get")
    @SaCheckLogin
    @SaCheckPermission("batch:view")
    public TaskLogVo get(
            Long id
    ) {
        User user = UserUtil.get();
        Role role = UserUtil.getRole();
        TaskLogVo taskLogVo = taskLogService.getById(id);
        UserUtil.baseVerifyUser(
                role,
                user.getId(),
                taskLogVo.getUserId(),
                user.getParentId(),
                userService.getParentIdById(taskLogVo.getUserId())
        );
        return taskLogVo;
    }


    @RestPack
    @GetMapping("infoPaging")
    @SaCheckPermission("batch:view")
    public Page<TaskLogInfoVo> infoPaging(
            Integer pageNumber,
            Integer pageSize,
            Long taskId
    ) {
        User user = UserUtil.get();
        Role role = UserUtil.getRole();
        TaskLog taskLog = taskLogService.getByIdOpt(taskId).orElseThrow(() -> new ProgramException("任务不存在"));
        UserUtil.baseVerifyUser(
                role,
                user.getId(),
                taskLog.getUserId(),
                user.getParentId(),
                userService.getParentIdById(taskLog.getUserId())
        );
        return taskLogInfoService.clientPaging(
                pageNumber,
                pageSize,
                user.getId(),
                role,
                taskId,
                null,
                null,
                null,
                null,
                null,
                null
        );

    }

}
